#ifndef __tenacitas_tester__tester__h__
#define __tenacitas_tester__tester__h__



/** @file */


/** @todo */

// ==> C++ Headers
#include <string>
#include <map>
#include <functional>
#include <type_traits>
#include <cstdint>

// ==> 3rds Headers

// ==> Our Headers

// ==> Namespaces

// ==> Macro Constants

// ==> Macro Commands


/** ==> namespace of the organization */
namespace tenacitas {
    /** ==> namespace for the project */
    namespace tester {

        // ==> Typedefs 

        // ==> Pre-Declarations

        // ==> Attributes 

        // ==> Functions
        
        // ==> Classes

        struct no_for_all {
            typedef void * data_type;

            data_type create(){ return 0; }
            void destry(){ }
        };

        struct no_for_each {
            typedef void * data_type;

            data_type create(){ return 0; }
            void destry(){ }

        };

        /** Defines a group of tests

            @todo report for each test
            @todo report for the overall result
            @todo policy to decide if tests should stop if one test fails
            @nosubgrouping 
        */
        template<typename t_setup_type>
        class tests {
            

            // ==> tests - Friends

        public:

            // ==> tests - Public Internal Types

            typedef t_setup_type setup_type;

            typedef std::function<bool(const setup_type &)> test_type;

            // ==> tests - Public Constructors

            /** Default constructor */
            tests(){}
            
            /** Destructor */
            ~tests(){}

            /** not-allowed */
            tests(const tests &)= delete;

            /** not-allowed */
            tests(tests &&)= delete;


            // ==> tests - Public Accessors

            // ==> tests - Public Helpers

            // ==> tests - Public Processors

            /** Runs all the tests */
            void run(setup_type && p_setup){

                typename test_collection::const_iterator ite = m_tests.begin();
                typename test_collection::const_iterator end = m_tests.end();

                for (; ite != end; ++ite){
                    p_setup.initialize();                    
                    ite->second(p_setup);
                    p_setup.finalize();
                }
            }
    

            /** Runs a specific test */
            //            void run(const std::string & p_test_name);

            // ==> tests - Public Operators

            /** not-allowed */
            tests & operator = (const tests &)= delete;

            /** not-allowed */
            tests & operator = (tests &&)= delete;

            /** not-allowed */
            bool operator == (const tests &)const = delete;

            /** not-allowed */
            bool operator != (const tests &)const = delete;

            /** Adds a @p test to be executed

                @param p_test_name is the name of the test to be executed
                @param p_test is the method to be executed
            */

            void add(const std::string & p_test_name, test_type && p_test){
                m_tests.insert(std::pair<std::string, test_type>(p_test_name,
                                                                 std::move(p_test)));
            }

            // ==> tests - Public Attributes

        private:

            // ==> tests - Private Internal Types

            /** Type for the collection of @p test methods */
            typedef std::map<std::string, test_type> test_collection;

            // ==> tests - Private Attributes

            /** Collection of tests to execute */
            test_collection m_tests;
            
        };


    }
}



// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::tester;


#endif
